﻿using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace System.Security.Cryptography
{
    /// <summary>
    /// 对称加密服务。内含一个对称加密算法 SymmetricAlgorithm
    /// </summary>
    public class SymmetricCryptoService
    {
        /// <summary>
        /// 由 SymmetricCryptoServiceOption 创建的算法实例。
        /// </summary>
        [NotNull] 
        private readonly SymmetricAlgorithm _algorithm;

        /// <summary>
        /// 根据 option 创建一个对称加密算法。
        /// </summary>
        /// <param name="option">创建对称加密算法需要的配置信息</param>
        public SymmetricCryptoService(SymmetricCryptoServiceOption? option = null)
        {
            option ??= new SymmetricCryptoServiceOption();
            _algorithm = option.CreateAlgorithm();
        }
        ~SymmetricCryptoService()
        {
            _algorithm.Dispose();
        }

        /// <summary>
        /// 通过调用 Encode 方法实现。
        /// </summary>
        /// <param name="filePath"></param>
        public void EncodeFile(string filePath)
        {
            string input = File.ReadAllText(filePath);
            string encryptedText = Encode(input);
            File.WriteAllText(filePath, encryptedText);
        }

        /// <summary>
        /// 通过调用 Decode 方法实现。
        /// </summary>
        /// <param name="filePath"></param>
        public void DecodeFile(string filePath)
        {
            string input = File.ReadAllText(filePath);
            string decryptedText = Decode(input);
            File.WriteAllText(filePath, decryptedText);
        }

        /// <summary>
        /// UTF8 格式 将 input 转为 byte[]，再调用 EncryptBytes 进行加密，最后再转为 Latin1 字符串。
        /// </summary>
        /// <param name="input"></param>
        /// <returns>Base64字符串</returns>
        public string Encode(string input)
        {
            var inBytes = Encoding.UTF8.GetBytes(input);
            var outBytes = EncryptBytes(inBytes);
            return Encoding.Latin1.GetString(outBytes);
        }

        /// <summary>
        /// Latin1 格式 将 input 转为 byte[]，再调用 DecryptBytes 进行解密，最后再转为 UTF8 字符串。
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public string Decode(string input)
        {
            var inBytes = Encoding.Latin1.GetBytes(input);
            var outBytes = DecryptBytes(inBytes);
            return Encoding.UTF8.GetString(outBytes);
        }

        /// <summary>
        /// 调用 EncryptoTransform 进行加密实现。
        /// 先加密，再进行 Base64 转换。
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private byte[] EncryptBytes(byte[] input)
        {
            using MemoryStream inStream = new(input);
            using MemoryStream memoryStream = EncryptoTransform(inStream);
            using MemoryStream base64MemoryStream = EncryptoTransform(memoryStream, new ToBase64Transform());
            byte[] output = base64MemoryStream.ToArray();
            return output;
        }


        /// <summary>
        /// 调用 DecryptoTransform 实现解密。
        /// 先进行 Base64 转换，再解密。
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private byte[] DecryptBytes(byte[] input)
        {
            using MemoryStream inStream = new(input);
            using MemoryStream base64MemoryStream = DecryptoTransform(inStream, new FromBase64Transform());
            using MemoryStream memoryStream = DecryptoTransform(base64MemoryStream);
            return memoryStream.ToArray();
        }


        /// <summary>
        /// 使用 instream 和 _algorithm.CreateEncryptor() 为参数直接调用 EncryptoTransform。
        /// </summary>
        /// <param name="instream">用于传递给 EncryptoTransform 的参数之一</param>
        /// <returns></returns>
        private MemoryStream EncryptoTransform(Stream instream)
        {
            return EncryptoTransform(instream, _algorithm.CreateEncryptor());
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="instream">需要转换的 Stream</param>
        /// <param name="encryptor"></param>
        /// <returns>一个新的 MemoryStream </returns>
        private static MemoryStream EncryptoTransform(Stream instream, ICryptoTransform encryptor)
        {
            MemoryStream outstream = new();
            using (var stream = new CryptoStream(outstream, encryptor, CryptoStreamMode.Write, true))
            {
                instream.Position = 0;
                instream.CopyTo(stream);
                stream.FlushFinalBlock();
            }

            return outstream;
        }

        /// <summary>
        /// 使用 instream 和 _algorithm.CreateDecryptor() 为参数直接调用 DecryptoTransform。
        /// </summary>
        /// <param name="instream">用于传递给 DecryptoTransform 的参数之一</param>
        /// <returns></returns>
        private MemoryStream DecryptoTransform(Stream instream)
        {
            return DecryptoTransform(instream, _algorithm.CreateDecryptor());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instream">需要转换的 Stream</param>
        /// <param name="decryptor"></param>
        /// <returns>一个新的 MemoryStream </returns>
        private static MemoryStream DecryptoTransform(Stream instream, ICryptoTransform decryptor)
        {
            MemoryStream outstream = new();
            using (var stream = new CryptoStream(instream, decryptor, CryptoStreamMode.Read))
            {
                stream.CopyTo(outstream);
                outstream.Position = 0;
            }

            return outstream;
        }
    }
}